# @author: caijiaming
# @Time 2023/9/7 16:46

#导入pygame第三方库
import pygame
#导入系统函数
import sys
#导入pygame常量
from pygame.locals import *
import random
import codecs

#设置游戏屏幕大小
SCREENWIDTH = 480
SCREENHEIGHT = 800

#初始化pygame
pygame.init()
#设置窗体主窗体
screen = pygame.display.set_mode((SCREENWIDTH,SCREENHEIGHT))
#设置窗体标题
pygame.display.set_caption("彩图版飞机大战")
#设置游戏图标
ic_launcher = pygame.image.load("resources/image/ic_launcher.png").convert_alpha()
pygame.display.set_icon(ic_launcher)

#加载窗体背景图
background = pygame.image.load("resources/image/background.png").convert_alpha()

#引用各种图片资源

#游戏结束图片
game_over = pygame.image.load("resources/image/gameover.png").convert_alpha()
#子弹图片
plane_bullet = pygame.image.load("resources/image/bullet.png")
#玩家飞机图片
player_img1 = pygame.image.load("resources/image/player1.png")
player_img2 = pygame.image.load("resources/image/player2.png")
player_img3 = pygame.image.load("resources/image/player_off1.png")
player_img4 = pygame.image.load("resources/image/player_off2.png")
player_img5 = pygame.image.load("resources/image/player_off3.png")

#敌机飞机图片
enemy_img1 = pygame.image.load("resources/image/enemy1.png")
enemy_img2 = pygame.image.load("resources/image/enemy2.png")
enemy_img3 = pygame.image.load("resources/image/enemy3.png")
enemy_img4 = pygame.image.load("resources/image/enemy4.png")

#创建游戏精灵

#子弹类,集成pygame精灵类
class Bullet(pygame.sprite.Sprite):
    #初始化bullet_img 子弹图片，init_pos：子弹初始化坐标
    def __init__(self,bullet_img,init_pos):
        #调用父类的初始化方法sprite的属性
        pygame.sprite.Sprite.__init__(self)
        self.image = bullet_img
        #获取子弹图片区域
        self.rect = self.image.get_rect()
        #初始化子弹出现位置
        self.rect.midbottom = init_pos
        #初始化子弹移动速度
        self.speed = 5

    #子弹移动方法
    def move(self):
        self.rect.top -= self.speed

#玩家飞机类,继承pygame.sprite.Sprite类
class Player(pygame.sprite.Sprite):
    #初始化工作
    def __init__(self,player_rect,init_pos):
        #调用精灵父类初始化玩家类
        pygame.sprite.Sprite.__init__(self)
        #用与存储玩家飞机列表
        self.image = []
        for i in range(len(player_rect)):
            self.image.append(player_rect[i].convert_alpha())

        #初始化图片所在矩形
        self.rect = player_rect[0].get_rect()
        #初始化矩形的左上角
        self.rect.topleft = init_pos
        #初始化玩家飞机速度
        self.speed = 8
        #玩家飞机发射子弹集合
        self.bullets = pygame.sprite.Group()
        #玩家飞机图片索引
        self.img_index = 0
        #玩家是否被击中
        self.is_hit = False


    #玩家发射子弹
    def shoot(self,bullet_img):
        bullet = Bullet(bullet_img,self.rect.midtop)
        #添加子弹到子弹精灵集合中
        self.bullets.add(bullet)

    #玩家飞机向上移动，需判断是否移出边界
    def moveUp(self):
        if self.rect.top <= 0:
            self.rect.top = 0
        else:
            self.rect.top -= self.speed

    #玩家飞机向下移动，需判断是否移出边界
    def moveDown(self):
        if self.rect.top >= SCREENHEIGHT - self.rect.height:
            self.rect.top = SCREENHEIGHT - self.rect.height
        else:
            self.rect.top += self.speed

    #玩家飞机向左移动,需判断是否移出边界
    def moveLeft(self):
        if self.rect.left <= 0:
            self.rect.left = 0
        else:
            self.rect.left -= self.speed

    #玩家飞机向右移动，需判断是否移出边界
    def moveRight(self):
        if self.rect.left >= SCREENWIDTH - self.rect.width:
            self.rect.left = SCREENWIDTH - self.rect.width
        else:
            self.rect.left += self.speed


#敌机类，继承pygame.sprite.Sprite类
class Enemy(pygame.sprite.Sprite):
    #初始化
    def __init__(self,enemy_img,enemy_down_imgs,init_pos):
        pygame.sprite.Sprite.__init__(self)
        #敌机图片
        self.image = enemy_img
        #敌机图片矩形尺寸
        self.rect = self.image.get_rect()
        #敌机出现位置
        self.rect.topleft = init_pos
        #敌机被击落图片
        self.down_imgs = enemy_down_imgs
        #敌机速度
        self.speed = 2
        #敌机被击落默认状态图片索引
        self.down_index = 0

    #敌机移动,向下移动
    def move(self):
        self.rect.top += self.speed


def write_txt(content,strim,path):
    f = codecs.open(path,strim,"utf8")
    f.write(str(content))
    f.close()

def read_txt(path):
    with open(path,"r",encoding="utf8") as f:
        lines = f.readlines()
    return lines

#玩家分数排行榜
def gameRanking():
    screen2 = pygame.display.set_mode((SCREENWIDTH,SCREENHEIGHT))
    #绘制背景
    screen2.fill(0)
    screen2.blit(background,(0,0))
    #使用系统文字
    xtfont = pygame.font.SysFont("songti",30)
    #排行榜标题
    textstart = xtfont.render("排行榜",True,(255,0,0))
    text_rect = textstart.get_rect()
    text_rect.centerx = screen.get_rect().centerx
    text_rect.centery = 50
    screen2.blit(textstart,text_rect)
    #重新开始按钮
    textstart = xtfont.render("重新开始",True,(255,0,0))
    text_rect = textstart.get_rect()
    text_rect.centerx = screen.get_rect().centerx
    text_rect.centery = screen.get_rect().centery + 120
    screen2.blit(textstart,text_rect)
    #获取排行榜文档内容
    arrayscore = read_txt("score.txt")[0].split("mr")
    #循环排行榜文件显示排行
    for i in range(0,len(arrayscore)):
        #游戏结束后显示的最终分数
        font = pygame.font.Font(None,48)
        #排名从1到10
        k = i+1
        text = font.render(str(k)+" "+arrayscore[i].replace("\n",""),True,(255,0,0))
        text_rect = text.get_rect()
        text_rect.centerx = screen2.get_rect().centerx
        text_rect.centery = 80 + 30 * k
        #绘制分数
        screen2.blit(text,text_rect)


#开始游戏主函数
def startGame():
    #设置游戏循环帧率
    clock = pygame.time.Clock()
    #设置玩家飞机不同状态的图片列表
    player_rect = []
    #玩家飞机图片
    player_rect.append(player_img1)
    player_rect.append(player_img2)
    #玩家飞机被击中，飞机爆炸图片
    player_rect.append(player_img2)
    player_rect.append(player_img3)
    player_rect.append(player_img4)
    player_rect.append(player_img5)
    player_pos = [200,600]
    #初始化玩家飞机
    player = Player(player_rect,player_pos)
    #子弹图片
    bullet_img = plane_bullet
    #敌机不同状态的下的图片列表，多张图片展示为动画效果
    enemy1_img = enemy_img1
    enemy1_rect = enemy1_img.get_rect()
    enemy1_down_imgs = []
    enemy1_down_imgs.append(enemy_img1)
    enemy1_down_imgs.append(enemy_img2)
    enemy1_down_imgs.append(enemy_img3)
    enemy1_down_imgs.append(enemy_img4)
    #储存敌机
    enemies1 = pygame.sprite.Group()
    #储存被击毁的敌机，用来渲染击毁动画
    enemies_down = pygame.sprite.Group()
    #初始化射击及敌机移动频率
    shoot_frequency = 0
    enemy_frequency = 0
    #玩家被击中后的效果处理
    player_down_index = 16
    #初始化分数
    score = 0
    #判断游戏退出循环
    running = True
    while running:
        #绘制屏幕
        screen.fill(0)
        screen.blit(background,(0,0))

        #生成子弹，需要控制生成频率
        #如果玩家飞机没被击中，则可发射子弹
        if not player.is_hit:
            if shoot_frequency % 15 == 0:
                player.shoot(bullet_img)
            shoot_frequency += 1
            if shoot_frequency >= 15:
                shoot_frequency = 0

        #子弹效果处理
        for bullet in player.bullets:
            #以固定速度移动子弹
            bullet.move()
            #移出屏幕后删除子弹
            if bullet.rect.bottom < 0:
                player.bullets.remove(bullet)

        #显示子弹
        player.bullets.draw(screen)

        #生成敌机
        if enemy_frequency % 50 == 0:
            enemy_pos = [random.randint(0,SCREENWIDTH - enemy1_rect.width),0]
            enemy1 = Enemy(enemy1_img,enemy1_down_imgs,enemy_pos)
            enemies1.add(enemy1)
        enemy_frequency += 1
        if enemy_frequency >= 100:
            enemy_frequency = 0
        for enemy in enemies1:
            #移动敌机
            enemy.move()
            #敌机与玩家飞机碰撞效果处理，两个精灵之间的圆检测
            if pygame.sprite.collide_circle(enemy,player):
                #碰撞，敌机爆炸，加入到敌机被击毁的精灵组中
                enemies_down.add(enemy)
                #敌机被击毁,生成敌机的精灵组减少对应敌机
                enemies1.remove(enemy)
                #玩家飞机被击中
                player.is_hit = True
                break

            #移除屏幕后删除飞机
            if enemy.rect.top < 0:
                enemies1.remove(enemy)

        #敌机被子弹击中效果处理
        #将被击中的敌机对象添加到击毁敌机的group中，用来渲染动画
        #方法groupcollide()是检测两个精灵组中精灵们的矩形碰撞，若有碰撞返回，一个字典，否则返回none
        enemies1_down = pygame.sprite.groupcollide(enemies1,player.bullets,1,1)
        #遍历key值，返回碰撞的敌机
        for enemy_down in enemies1_down:
            #添加销毁的敌机到精灵组列表
            enemies_down.add(enemy_down)

        #绘制玩家飞机

        #判断玩家飞机是否被击中
        if not player.is_hit:
            screen.blit(player.image[player.img_index],player.rect)
            #更换图片索引，使飞机有东环效果
            player.img_index = shoot_frequency // 8
        else:
            #玩家飞机被击中效果处理
            player.img_index = player_down_index // 8
            screen.blit(player.image[player.img_index],player.rect)
            player_down_index += 1
            if player_down_index > 47:
                #击中效果处理完成后游戏结束
                running = False

        #敌机被子弹击中效果显示
        for enemy_down in enemies_down:
            if enemy_down.down_index == 0:
                pass
            if enemy_down.down_index > 7:
                enemies_down.remove(enemy_down)
                score += 100
                continue
            #显示碰撞图片
            screen.blit(enemy_down.down_imgs[enemy_down.down_index // 2],enemy_down.rect)
            enemy_down.down_index += 1

        #显示精灵
        enemies1.draw(screen)

        #绘制当前得分
        score_font = pygame.font.Font(None,36)
        score_text = score_font.render(str(score),True,(255,255,255))
        text_rect = score_text.get_rect()
        text_rect.topleft = [10,10]
        screen.blit(score_text,text_rect)

        # 控制游戏最大帧率为60
        clock.tick(60)
        # 更新屏幕
        pygame.display.update()
        # 判断游戏事件
        for event in pygame.event.get():
            if event.type == QUIT:
                # 退出游戏,并关闭窗体
                pygame.quit()
                # 关闭窗体
                sys.exit()

        #玩家按键盘操作飞机
        key_press = pygame.key.get_pressed()
        if key_press[K_w] or key_press[K_UP]:
            player.moveUp()
        if key_press[K_s] or key_press[K_DOWN]:
            player.moveDown()
        if key_press[K_a] or key_press[K_LEFT]:
            player.moveLeft()
        if key_press[K_d] or key_press[K_RIGHT]:
            player.moveRight()

    # 绘制游戏结束背景
    screen.blit(game_over, (0, 0))
    # 游戏 Game Over 后显示最终得分
    font = pygame.font.Font(None,48)
    #使用render方法获取一个新的surface对象
    text = font.render('Score:' + str(score),True,(255,255,255))
    #获取文本矩形
    text_rect = text.get_rect()
    #设置文本的中心横坐标
    text_rect.centerx = screen.get_rect().centerx
    # 设置文本的中心纵坐标
    text_rect.centery = screen.get_rect().centery + 24
    #添加字体到主屏幕
    screen.blit(text, text_rect)
    # # 使用系统字体
    xtfont = pygame.font.SysFont('songti', 30)
    #重新开始按钮
    textstart = xtfont.render('重新开始 ', True, (255, 255, 255))
    text_start_rect = textstart.get_rect()
    text_start_rect.centerx = screen.get_rect().centerx
    text_start_rect.centery = screen.get_rect().centery + 120
    screen.blit(textstart, text_start_rect)
    #排行榜按钮
    textrank = xtfont.render('排行榜 ', True, (255, 255, 255))
    textrank_rect = textstart.get_rect()
    textrank_rect.centerx = screen.get_rect().centerx
    textrank_rect.centery = screen.get_rect().centery + 180
    screen.blit(textrank, textrank_rect)

    #判断得分，更新排行榜
    #临时的变量在到排行榜的时候使用
    j = 0
    #获取文件中内容转换成列表使用mr分割开内容
    arrayscore = read_txt("score.txt")[0].split("mr")
    for i in range(0,len(arrayscore)):
        #判断当前得分是否大于排行榜上的分数
        if score > int(arrayscore[i].replace("\n","")):
            j = arrayscore[i]
            arrayscore[i] = str(score)
            score = 0
        #替换下来的分数下移动一位
        if int(j) > int(arrayscore[i].replace("\n","")):
            k = arrayscore[i]
            arrayscore[i] = str(j)
            j = k

    #循环分数列表，写入文档
    for i in range(0,len(arrayscore)):
        #判断列表中第一个分数
        if i == 0:
            #覆盖写入内容，并追加mr分割符
            write_txt(arrayscore[i]+"mr","w","score.txt")
        else:
            #判断是否最后一个分数,是，不需要加入mr分隔符
            if i == 9:
                write_txt(arrayscore[i],"a","score.txt")
            else:
                write_txt(arrayscore[i] + "mr","a","score.txt")


#调用方法，开始游戏
startGame()

#游戏结束，循环更新页面
while True:
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()

        if event.type == MOUSEBUTTONDOWN:
            #判断鼠标单击的位置是否为开始按钮位置范围内
            #计算鼠标点击坐标是否在指定按钮的坐标范围内，重新开始按钮
            if screen.get_rect().centerx - 70 <= event.pos[0] \
                    and event.pos[0] <= screen.get_rect().centerx + 50 \
                    and screen.get_rect().centery + 100 <= event.pos[1] \
                    and screen.get_rect().centery + 140 >= event.pos[1]:
                # 重新开始游戏
                startGame()

            #计算鼠标点击坐标是否在指定按钮的坐标范围内，排行榜按钮
            if screen.get_rect().centerx - 70 <= event.pos[0] \
                    and event.pos[0] <= screen.get_rect().centerx + 50 \
                    and screen.get_rect().centery + 160 <= event.pos[1] \
                    and screen.get_rect().centery + 200 >= event.pos[1]:
                # 显示排行榜
                gameRanking()
    #更新页面
    pygame.display.update()